package com.example.cc.client.manager;

import cn.hutool.core.map.MapUtil;
import com.example.cc.common.constants.ConfigConstants;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * 通用缓存管理器，使用 ConcurrentHashMap 实现
 *
 * @param <K>
 * @param <V>
 */
@Slf4j
@SuppressWarnings("all")
public class GenericCacheManager<K, V> implements CacheManager<K, V> {

    private final ConcurrentHashMap<K, CacheEntry<V>> cache = new ConcurrentHashMap<>();
    private final long defaultTtl = ConfigConstants.TTL_SECOND_ONE_DAY;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public GenericCacheManager() {
        scheduler.scheduleAtFixedRate(this::cleanupExpiredData, 1, 10, TimeUnit.MINUTES);
    }

    public void update(K key, V value) {
        put(key, value, defaultTtl, TimeUnit.SECONDS);
    }

    @Override
    public void delete(K key) {
        cache.remove(key);
    }

    @Override
    public void updateCache(Map<K, V> map) {
        if (MapUtil.isNotEmpty(map)) {
            map.forEach(this::update);
        }
    }

    public void put(K key, V value, long ttl, TimeUnit timeUnit) {
        long expireTime = System.nanoTime() + timeUnit.toNanos(ttl);
        CacheEntry<V> entry = new CacheEntry<>(value, expireTime);
        cache.put(key, entry);
    }

    public V get(K key) {
        CacheEntry<V> entry = cache.get(key);
        if (entry != null && !entry.isExpired()) {
            return entry.getValue();
        } else {
            invalidate(key);
            return null;
        }
    }

    public Map<K, V> getAll() {
        return cache.entrySet().stream()
                .filter(entry -> !entry.getValue().isExpired())
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getValue()));
    }

    public void invalidate(K key) {
        cache.remove(key);
    }

    public void clear() {
        cache.clear();
    }

    private void cleanupExpiredData() {
        cache.entrySet().removeIf(entry -> entry.getValue().isExpired());
    }

    @Data
    private static class CacheEntry<V> {

        private final V value;

        /**
         * 过期时间戳
         */
        private final AtomicLong expireTime;

        public CacheEntry(V value, long expireTime) {
            this.value = value;
            this.expireTime = new AtomicLong(expireTime);
        }

        public boolean isExpired() {
            return System.nanoTime() > expireTime.get();
        }
    }

    public void shutdown() {
        scheduler.shutdown();
    }


    public static void main(String[] args) throws InterruptedException {
        GenericCacheManager<String, String> cacheManager = new GenericCacheManager<>();

        cacheManager.update("key1", "value1");
        cacheManager.put("key2", "value2", 10, TimeUnit.SECONDS);

        String value1 = cacheManager.get("key1");
        System.out.println("value1: " + value1);

        Map<String, String> allItems = cacheManager.getAll();
        System.out.println("All items in cache: " + allItems);

        Thread.sleep(TimeUnit.SECONDS.toMillis(10));
        String value2 = cacheManager.get("key2");
        System.out.println("Retrieved value2 after expiration: " + value2);

        cacheManager.clear();

        cacheManager.shutdown();
    }
}
